package com.abc.cloud.service.impl;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import com.abc.cloud.common.enums.FileCategoryEnums;
import com.abc.cloud.common.enums.FileTypeEnums;
import com.abc.cloud.common.exception.MyException;
import com.abc.cloud.common.utils.*;
import com.abc.cloud.dto.count.UserFileCount;
import com.abc.cloud.dto.file.FileCountItem;
import com.abc.cloud.entity.UserEntity;
import com.abc.cloud.service.FileTagService;
import com.abc.cloud.service.UserService;
import com.abc.cloud.vo.file.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.abc.cloud.dao.FileDao;
import com.abc.cloud.entity.FileEntity;
import com.abc.cloud.service.FileService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static com.abc.cloud.common.constants.Constants.*;


@Slf4j
@Service("fileService")
public class FileServiceImpl extends ServiceImpl<FileDao, FileEntity> implements FileService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private FileService fileService;

    @Autowired
    private UserService userService;

    @Autowired
    private FileTagService fileTagService;

    @Autowired
    private SpaceUtil spaceUtil;

    @Value("${ipaddr}")
    private String ipAddr;

    @Override
    public PageUtil queryPage(Map<String, Object> params) {
        QueryWrapper<FileEntity> fileEntityQueryWrapper = new QueryWrapper<>();
        fileEntityQueryWrapper.eq("status", 2);
        IPage<FileEntity> page = this.page(
                new Query<FileEntity>().getPage(params, "recovery_time",false),
                fileEntityQueryWrapper
        );

        return new PageUtil(page);
    }


    @Transactional
    @Override
    public Boolean uploadChunk(FileUpload fileUpload) {
        Long userId = UserHolder.getUser().getId();
        try {
            // 文件路径
            String filePath = ossPath(userId, fileUpload.getFileMd5());
            File file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();
            }
            // 文件分块路径
            String fileChunkPath = filePath + "/chunk";
            File file1 = new File(fileChunkPath);
            if (!file1.exists()) {
                file1.mkdirs();
            }
            fileUpload.getFile().transferTo(new File(fileChunkPath + File.separator + fileUpload.getChunkIndex()));
            // 缓存redis
            redisTemplate.opsForList().rightPush(USER_UPLOAD_CHUNK + userId + fileUpload.getFileMd5(), String.valueOf(fileUpload.getChunkIndex()));
            // 减少内存
            fileService.reduceSpace(fileUpload.getFile().getSize(), userId);

        } catch (Exception e) {
            log.error("文件分片上传出错{}", e.toString());
            throw new MyException(e.toString());
        }
        return true;
    }

    @Transactional
    @Override
    public void reduceSpace(Long fileSize, Long userId) {
        // 消耗系统内存
        Boolean flag = spaceUtil.changeSystemSpace(fileSize);
        if (!flag) {
            throw new MyException("系统内存不足");
        }

        // 消耗用户内存
        flag = userService.saveFile(userId, fileSize);
        if (!flag) {
            throw new MyException("用户内存不足");
        }
    }

    @Override
    public Map<String, Object> checkChunk(FileUploadCheck fileUploadCheck) {
        Map<String, Object> res = new HashMap<>();
        // 检查文件是否已经上传 ---> 秒传
        List<FileEntity> fileList = this.baseMapper.selectList(new QueryWrapper<FileEntity>().eq("file_md5", fileUploadCheck.getFileMd5()));
        if (fileList.size() != 0) {
            FileEntity file = fileList.get(0);
            res.put("success", true);
            res.put("message", "秒传");
            res.put("skipUpload", true);
            // 将文件信息的保存到用户名下
            Long userId = UserHolder.getUser().getId();
            file.setUserId(userId);
            file.setStatus(1);
            file.setPublicType(1);
            file.setCommentType(1);
            file.setPId(fileUploadCheck.getFolderId());
            file.setFilename(fileUploadCheck.getFilename());
            file.setFileSize(fileUploadCheck.getFileSize());
            this.baseMapper.insert(file);
            // 减少内存
            fileService.reduceSpace(fileUploadCheck.getFileSize(), userId);

            // 添加文件关键字
            List<String> names = new ArrayList<>();
            names.add(file.getFilename());

            FileTagVo fileTagVo = new FileTagVo();
            fileTagVo.setFileId(file.getId());
            fileTagVo.setNames(names);

            fileTagService.saveFileTag(fileTagVo);

            return res;
        }

        // 查询已经上传的文件块 ---> 断点续传
        Long userId = UserHolder.getUser().getId();
        Long length = redisTemplate.opsForList().size(USER_UPLOAD_CHUNK + userId + fileUploadCheck.getFileMd5());
        List<Long> listData = redisTemplate.opsForList().range(USER_UPLOAD_CHUNK + userId + fileUploadCheck.getFileMd5(), 0, length - 1).stream().map(item -> Long.valueOf(item)).collect(Collectors.toList());

//        // 减少内存
//        fileService.reduceSpace(fileUploadCheck.getFileSize() * length, userId);

        res.put("success", true);
        res.put("message", "断点续传");
        res.put("uploaded", listData);
        res.put("skipUpload", false);

        return res;
    }

    @Override
    public Boolean mergeChunks(FileMerge fileMerge, FileEntity fileEntity) {
        Long userId = UserHolder.getUser().getId();

        // ====合并文件====
        // oss存储路口
        String filePath = ossPath(userId, fileMerge.getFileMd5());
        // 文件分块路径
        String fileChunkPath = filePath + "/chunk";
        // 文件后缀
        String suffix = fileMerge.getFilename().substring(fileMerge.getFilename().lastIndexOf("."));
        // 文件最终保存路径
        String fileSavePath = filePath + File.separator + fileMerge.getFileMd5() + suffix;
        File fileChunkFolder = new File(fileChunkPath);
        File saveFile = new File(fileSavePath);
        File[] chunks = fileChunkFolder.listFiles();

        // 使用自定义的Comparator按文件名转为数字排序
        Arrays.sort(chunks, new Comparator<File>() {
            @Override
            public int compare(File file1, File file2) {
                // 提取文件名中的数字部分进行比较
                Long number1 = Long.valueOf(file1.getName());
                Long number2 = Long.valueOf(file2.getName());
                return Long.compare(number1, number2);
            }
        });

        try {
            // 随机输入/输出流
            RandomAccessFile randomAccessFileWriter = new RandomAccessFile(saveFile, "rw");
            byte[] bytes = new byte[1024];
            for (File chunk : chunks) {
                RandomAccessFile randomAccessFileReader = new RandomAccessFile(chunk, "r");
                int len;
                while ((len = randomAccessFileReader.read(bytes)) != -1) {
                    randomAccessFileWriter.write(bytes, 0, len);
                }
                randomAccessFileReader.close();
            }
            randomAccessFileWriter.close();
        } catch (Exception e) {
            throw new MyException("合并文件出错");
        }

        // ====校验文件是否正确====
        try (FileInputStream fileInputStream = new FileInputStream(saveFile)) {
            // 计算合并后文件的md5
            String mergeFileMd5 = DigestUtils.md5Hex(fileInputStream);
            fileInputStream.close();
            // 比较原始md5和合并后文件的md5
            if (!fileMerge.getFileMd5().equals(mergeFileMd5)) {
                log.error("校验合并文件md5值不一致,原始文件:{},合并文件:{}", fileMerge.getFileMd5(), mergeFileMd5);
                throw new MyException("文件校验失败");
            }
            fileEntity.setFileSize(saveFile.length());
        } catch (Exception e){
            throw new MyException("文件校验失败");
        }

        // ====将文件保存数据库====
        fileService.saveFile(fileEntity, fileMerge, userId, fileSavePath, suffix);

        // ====清理分块文件====
        FileSystemUtils.deleteRecursively(fileChunkFolder);

        return true;
    }

    /**
     * 将文件保存到数据库
     * @return
     */
    @Transactional
    @Override
    public void saveFile(FileEntity fileEntity, FileMerge fileMerge, Long userId, String fileSavePath, String suffix) {
        FileTypeEnums fileTypeEnums = FileTypeEnums.getBySuffix(suffix);
        LocalDateTime now = LocalDateTime.now();
        fileEntity.setUserId(userId);
        fileEntity.setUsername(UserHolder.getUser().getUsername());
        fileEntity.setCommentType(1);
        fileEntity.setFilePath(fileSavePath);
        fileEntity.setPublicType(1);
        fileEntity.setFolderType(1);

        // 若分类为空，就说明是其他文件
        if (fileTypeEnums == null){
            fileEntity.setCategory(5);
            fileEntity.setFileType(11);
        }
        else{
            fileEntity.setCategory(fileTypeEnums.getCategory().getCategory());
            fileEntity.setFileType(fileTypeEnums.getType());
        }

        fileEntity.setCreateTime(now);
        fileEntity.setUpdateTime(now);
        fileEntity.setStatus(1);
        this.baseMapper.insertFile(fileEntity);

        // 新增文件标签，默认标签为文件名
        List<String> names = new ArrayList<>();
        names.add(fileEntity.getFilename());

        FileTagVo fileTagVo = new FileTagVo();
        fileTagVo.setFileId(fileEntity.getId());
        fileTagVo.setNames(names);

        fileTagService.saveFileTag(fileTagVo);

    }

    @Override
    public PageUtil getFolderFile(Map<String, Object> params) {
        Long userId = UserHolder.getUser().getId();

        Integer currPage = Integer.parseInt((String) params.get("page"));
        Integer limit = Integer.parseInt((String) params.get("limit"));

        params.put("userId", userId);
        params.put("page", (currPage - 1) * limit);
        params.put("limit", limit);
        List<FileEntity> fileEntityRespList = this.baseMapper.queryFolderFile(params);
        Integer countPage = this.baseMapper.countFolderFile(params);

        PageUtil pageUtil = new PageUtil(fileEntityRespList, limit, currPage, countPage,(int) Math.ceil(countPage * 1.0 / limit));

        return pageUtil;
    }

    private PageUtil queryPlus(Map<String, Object> params) {
        QueryWrapper<FileEntity> queryWrapper = new QueryWrapper();
        queryWrapper.eq("status", params.get("status"));

        // 只有分类为0的时候才会出现文件夹
        String category = (String) params.get("category");
        if(category != null && !category.equals("0")){
            queryWrapper.eq("category", Long.parseLong(category));
        }
        else{
            queryWrapper.eq("p_id", Long.parseLong((String) params.get("pId")));
        }

        String publicType = (String) params.get("publicType");
        Long userId = UserHolder.getUser().getId();
        if(publicType == null || publicType.equals("1")){
            queryWrapper.eq("user_id", userId);
        }
        else{
            queryWrapper.eq("public_type", Long.parseLong(publicType));
            // 判断是否是查询个人的公共文件
            String self = (String) params.get("self");
            if (self.equals("2")){
                queryWrapper.eq("user_id", userId);
            }
        }


        String key = (String) params.get("key");
        if(key != null){
            queryWrapper.and(item -> {
                item.like("filename", key).or().like("username", key);
            });
        }

        IPage<FileEntity> page = this.page(
                new Query<FileEntity>().getPage(params, "folder_type", false),
                queryWrapper
        );

        return new PageUtil(page);
    }

    @Transactional
    @Override
    public void saveFolder(FileSave fileSave) {
        UserEntity userEntity =  UserHolder.getUser();
        Long userId = userEntity.getId();
        String username = userEntity.getUsername();
        LocalDateTime now = LocalDateTime.now();
        if(fileSave.getId() == -1){
            // 新增文件夹
            FileEntity fileEntity = new FileEntity();
            fileEntity.setFolderType(2);
            fileEntity.setPublicType(1);
            fileEntity.setCommentType(1);
            fileEntity.setPId(fileSave.getFolderId());
            fileEntity.setUsername(username);
            fileEntity.setFilename(fileSave.getFilename());
            fileEntity.setUserId(userId);
            fileEntity.setStatus(1);
            fileEntity.setCreateTime(now);
            fileEntity.setUpdateTime(now);

            this.baseMapper.insertFile(fileEntity);

            // 新增文件标签，默认为文件名
            List<String> names = new ArrayList<>();
            names.add(fileEntity.getFilename());

            FileTagVo fileTagVo = new FileTagVo();
            fileTagVo.setFileId(fileEntity.getId());
            fileTagVo.setNames(names);

            fileTagService.saveFileTag(fileTagVo);

        }
        else{
            // 修改文件
            FileEntity fileEntity = this.baseMapper.selectById(fileSave.getId());
            // 检验身份
            if (!fileEntity.getUserId().equals(userId)){
                throw new MyException("身份验证失败");
            }

            // 若是添加回收站则添加回收时间
            if(fileEntity.getStatus() == 2){
                fileEntity.setRecoveryTime(LocalDateTime.now());
            }
            if(fileSave.getFolderId() != null){
                fileEntity.setPId(fileSave.getFolderId());
            }
            if(fileSave.getFilename() != null){
                fileEntity.setFilename(fileSave.getFilename());
            }
            if(fileSave.getStatus() != null){
                fileEntity.setStatus(fileSave.getStatus());
                // 若是彻底删除，则需要恢复空间
                if (fileSave.getStatus() == 3){
                    // 若是文件夹，则需要找出子文件并处理
                    if (fileEntity.getFolderType() == 2){
                        finalDeleteFile(userId, fileEntity.getId());
                    }
                    else{
                        fileService.reduceSpace(-fileEntity.getFileSize(), userId);
                    }
                }
            }
            if(fileSave.getPublicType() != null){
                fileEntity.setPublicType(fileSave.getPublicType());
                Map<String, Object> params = new HashMap<>();
                params.put("userId", userId);
                params.put("fileId", fileEntity.getId());
                params.put("publicType", fileSave.getPublicType());

                // 更新相邻文件夹状态
                if (fileEntity.getFolderType() == 2){
                    // 若是父文件夹，则更新子文件的公共状态
                    fileService.updateChildUnique(params);
                }

                // 若是子文件，则更新父文件的公共状态
                if (fileSave.getPublicType() == 2 && fileEntity.getPId() != 0){
                    params.put("fileId", fileEntity.getPId());

                    // 只有开启公共设置的时候才能更改父文件状态，关闭时不需要设置
                    fileService.updateParentUnique(params);
                }
            }
            if(fileSave.getCommentType() != null){
                fileEntity.setCommentType(fileSave.getCommentType());
            }
            if (fileSave.getParentId() != null){
                fileEntity.setPId(fileSave.getParentId());
            }
            this.baseMapper.updateById(fileEntity);
        }
    }

    @Transactional
    @Override
    public void updateChildUnique(Map<String, Object> params) {
        Long userId = Long.parseLong(params.get("userId").toString());
        Long fileId = Long.parseLong(params.get("fileId").toString());
        Integer publicType = (Integer) params.get("publicType");

        List<FileEntity> fileEntityList = this.baseMapper.selectList(new QueryWrapper<FileEntity>().eq("user_id", userId).eq("p_id", fileId));
        for (FileEntity fileEntity : fileEntityList) {
            fileEntity.setPublicType(publicType);
            this.baseMapper.updateById(fileEntity);
            if (fileEntity.getFolderType() == 2){
                params.put("fileId", fileEntity.getId());
                fileService.updateChildUnique(params);
            }
        }
    }

    @Transactional
    @Override
    public void updateParentUnique(Map<String, Object> params) {
        Long userId = Long.parseLong(params.get("userId").toString());
        Long fileId = Long.parseLong(params.get("fileId").toString());
        Integer publicType = (Integer) params.get("publicType");

        FileEntity fileEntity = this.baseMapper.selectOne(new QueryWrapper<FileEntity>().eq("user_id", userId).eq("id", fileId));
        fileEntity.setPublicType(publicType);
        System.out.println(fileEntity);
        this.baseMapper.updateById(fileEntity);
        if (fileEntity.getPId() != 0){
            params.put("fileId", fileEntity.getPId());
            fileService.updateParentUnique(params);
        }
    }


    @Override
    public void getFile(HttpServletResponse response, HttpServletRequest request, Long fileId, Long userId) {
        FileEntity fileEntity = this.baseMapper.selectById(fileId);
        if(fileEntity == null || fileEntity.getStatus() != 1){
            throw new MyException("文件不存在");
        }
        if(fileEntity.getPublicType() != null && fileEntity.getPublicType() == 1){
            if (userId == null || !userId.equals(fileEntity.getUserId())){
                throw new MyException("文件为私有文件，无法查看");
            }
        }

        String filePath = fileEntity.getFilePath();
        if(StrUtil.isBlank(filePath)){
            throw new MyException("文件路径不存在");
        }

        getFileStream(response, fileEntity.getFilename(), filePath);

    }

    @Override
    public void getFileStream(HttpServletResponse response, String filename, String filePath) {
        OutputStream out = null;
        FileInputStream in = null;
        try {
            File file = new File(filePath);
            if (!file.exists()) {
                return;
            }

            response.addHeader("Content-Length", "" + file.length());
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            response.setContentType("application/octet-stream");

            in = new FileInputStream(file);
            byte[] byteData = new byte[1024];
            out = response.getOutputStream();
            int len = 0;
            while ((len = in.read(byteData)) != -1) {
                out.write(byteData, 0, len);
            }
            out.flush();
        } catch (Exception e) {
            log.error("读取文件异常：{}", e.toString());
            throw new MyException("读取文件异常");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    log.error("IO异常", e);
                    throw new MyException("IO异常");
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error("IO异常", e);
                    throw new MyException("IO异常");
                }
            }
        }
    }


    @Transactional
    @Override
    public String upload(MultipartFile file, Long userId, String username) {
        String filename = file.getOriginalFilename();
        // 获取文件md5
        String fileMd5 = "";
        try {
            fileMd5 = DigestUtils.md5Hex(file.getInputStream());
        } catch (Exception e){
            log.info("用户{}，计算文件md5失败，文件名：{}", userId, filename);
            throw new MyException("计算文件MD5失败");
        }

        // 查询文件是否已经上传过了
        LocalDateTime now = LocalDateTime.now();
        FileEntity saveFile = new FileEntity();
        List<FileEntity> fileList = this.baseMapper.selectList(new QueryWrapper<FileEntity>().eq("file_md5", fileMd5));
        if (fileList.size() != 0) {
            FileEntity fileEntity = fileList.get(0);
            // 将文件信息的保存到用户名下
            saveFile.setUserId(userId);
            saveFile.setStatus(1);
            saveFile.setFileMd5(fileMd5);
            saveFile.setFilename(filename);
            saveFile.setFileSize(file.getSize());
            saveFile.setFilePath(fileEntity.getFilePath());
            saveFile.setPublicType(2);
            saveFile.setUsername(username);
            saveFile.setCreateTime(now);
            saveFile.setUpdateTime(now);

            this.baseMapper.insertFile(saveFile);

            return ipAddr + "/api/file/getFile/" + saveFile.getId();
        }

        try {
            // 保存文件
            // oss存储路口
            String filePath = ossPath(userId, fileMd5);
            // 文件后缀
            String suffix = filename.substring(filename.lastIndexOf("."));
            // 文件最终保存路径
            String fileSavePath = filePath + File.separator + fileMd5 + suffix;
            File fileFolderPath = new File(filePath);
            if (!fileFolderPath.exists()){
                fileFolderPath.mkdirs();
            }
            file.transferTo(new File(fileSavePath));

            // 将文件信息的保存到用户名下
            saveFile.setUserId(userId);
            saveFile.setStatus(1);
            saveFile.setFileMd5(fileMd5);
            saveFile.setFilename(filename);
            saveFile.setFileSize(file.getSize());
            saveFile.setFilePath(fileSavePath);
            saveFile.setPublicType(2);
            saveFile.setUsername(username);
            saveFile.setCreateTime(now);
            saveFile.setUpdateTime(now);

            this.baseMapper.insertFile(saveFile);
        } catch (Exception e){
            log.info("保存文件出错，错误原因：{}", e.getMessage());
            throw new MyException("保存文件出错");
        }

        return ipAddr + "/api/file/getFile/" + saveFile.getId();
    }


    /**
     * 获取用户文件类型统计
     * @param userId
     * @return
     */
    @Override
    public List<FileCountItem> getUserFileTypeCount(Long userId) {
        List<FileCountItem> res = new ArrayList<>();
        LinkedHashMap<String, FileTypeEnums> enumMap = EnumUtil.getEnumMap(FileTypeEnums.class);
        for (String key : enumMap.keySet()) {
            FileTypeEnums fileTypeEnum = enumMap.get(key);

            FileCountItem fileCountItem = new FileCountItem();
            fileCountItem.setFileType(fileTypeEnum.getDesc());
            fileCountItem.setCount(this.baseMapper.selectCount(new QueryWrapper<FileEntity>()
                    .eq("status", 1).eq("user_id", userId).eq("file_type", fileTypeEnum.getType())));

            res.add(fileCountItem);
        }

        return res;
    }

    /**
     * 冻结/解冻 用户单个文件
     * @param userId
     * @param fileId
     */
    @Transactional
    @Override
    public void updateUserFile(Long userId, Long fileId, Integer status) {
        FileEntity fileEntity = this.baseMapper.selectOne(new QueryWrapper<FileEntity>()
                .eq("user_id", userId).eq("file_id", fileId));
        fileEntity.setStatus(status);
        this.baseMapper.updateById(fileEntity);
    }

    /**
     * 冻结/解冻 用户全部文档
     * @param userId
     */
    @Transactional
    @Override
    public void updateUserFile(Long userId, Integer status) {
        this.baseMapper.updateUserAllFile(userId, status);
    }

    @Override
    public PageUtil adminGetFileList(Map<String, Object> params) {
        QueryWrapper<FileEntity> queryWrapper = new QueryWrapper<>();

        String key = (String) params.get("key");
        if (key != null){
            queryWrapper.and(item -> {
                item.like("filename", key).or().like("username", key);
            });
        }

        IPage<FileEntity> page = this.page(
                new Query<FileEntity>().getPage(params, "create_time", false),
                queryWrapper
        );

        return new PageUtil(page);
    }


    @Transactional
    @Override
    public void adminUpdateFile(FileAdminUpdate fileAdminUpdate) {
        FileEntity fileEntity = this.baseMapper.selectById(fileAdminUpdate.getId());
        BeanUtils.copyProperties(fileAdminUpdate, fileEntity);

        Integer folderType = fileEntity.getFolderType();
        if (folderType == 1){
            // 根据文件类型来更新category
            FileTypeEnums fileTypeEnums = FileTypeEnums.getByType(fileEntity.getFileType());
            FileCategoryEnums category = fileTypeEnums.getCategory();
            fileEntity.setCategory(category.getCategory());
        }

        this.baseMapper.updateById(fileEntity);
    }


    @Transactional
    @Override
    public void adminDeleteFile(List<Long> fileIds, Integer status) {
        this.baseMapper.deleteFileByIds(fileIds, status);
    }

    @Override
    public List<FileEntity> getFolderList(Map<String, Object> params) {
        Long pId = Long.parseLong((String) params.get("pId"));
        Long userId = UserHolder.getUser().getId();

        QueryWrapper<FileEntity> fileEntityQueryWrapper = new QueryWrapper<>();
        fileEntityQueryWrapper.eq("user_id", userId);
        fileEntityQueryWrapper.eq("p_id", pId);
        fileEntityQueryWrapper.eq("status", 1);
        fileEntityQueryWrapper.eq("folder_type", 2);
        fileEntityQueryWrapper.orderByDesc("create_time");

        return this.baseMapper.selectList(fileEntityQueryWrapper);
    }

    @Override
    public void adminUpdateFileComment(Long userId, Long fileId, Integer status) {
        FileEntity fileEntity = this.baseMapper.selectById(fileId);
        if (!userId.equals(fileEntity.getUserId())){
            throw new MyException("文件信息有误，请联系管理员");
        }
        fileEntity.setCommentType(status);
        this.baseMapper.updateById(fileEntity);
    }

    @Override
    public void adminUpdateFileComment(Long userId, Integer status) {
        this.baseMapper.updateUserFileComment(userId, status);
    }

    @Transactional
    @Override
    public void saveFileToMe(Long fileId, Long saveFileId, LocalDateTime now, String username, Long userId, Long fromUserId) {
        FileEntity fileEntity = fileService.getById(fileId);

        if (fileEntity.getUserId().equals(userId)){
            throw new MyException("不能保存自己的文件");
        }
        if (!fileEntity.getUserId().equals(fromUserId)){
            throw new MyException("文件出错，请联系管理员");
        }

        // 保存文件
        fileEntity.setId(null);
        fileEntity.setPId(saveFileId);
        fileEntity.setUserId(userId);
        fileEntity.setUsername(username);
        fileEntity.setCreateTime(now);
        fileEntity.setUpdateTime(now);
        fileEntity.setCommentType(1);
        fileEntity.setPublicType(1);
        this.baseMapper.insertFile(fileEntity);

        // 如果是文件就继续保存
        if (fileEntity.getFolderType() == 2){
            List<FileEntity> list = fileService.list(new QueryWrapper<FileEntity>().eq("status", 1).eq("p_id", fileId));
            for (FileEntity entity : list) {
                fileService.saveFileToMe(entity.getId(), fileEntity.getId(), now, username, userId, fromUserId);
            }
        }
        else{
            // 否则是文件的话就减少内存
            fileService.reduceSpace(fileEntity.getFileSize(), userId);
        }

    }

    @Override
    public List<UserFileCount> countUserFile(Long userId) {
        return this.baseMapper.countUserFile(userId);
    }

    /**
     * 获取项目文件路径
     *
     * @return
     */
    private String projectPath() {
        return System.getProperty("user.dir");
    }

    /**
     * 获取oss用户保存路径
     *
     * @param userId
     * @param md5
     * @return
     */
    private String ossPath(Long userId, String md5) {
        return projectPath() + "/resources/" + userId + File.separator + md5;
    }

    /**
     * 彻底删除文件，恢复内存空间
     * @param userId
     * @param fileId
     */
    private void finalDeleteFile(Long userId, Long fileId) {
        List<FileEntity> fileEntityList = this.baseMapper.selectList(new QueryWrapper<FileEntity>().eq("status", 1)
                .eq("p_id", fileId));
        // 无需更新子文件状态，因为父文件删除子文件必然不能展示，只需要恢复子文件占用的内存空间
        for (FileEntity entity : fileEntityList) {
            if (entity.getFolderType() == 1){
                fileService.reduceSpace(-entity.getFileSize(), userId);
            }
            else{
                this.finalDeleteFile(userId, entity.getId());
            }
        }
    }

}